#include "DataControl.hpp"

DataBuffer::DataBuffer()
{
    ptrDataBuffer=new std::map<int32_t,std::list<DynamicStruct>>();
}

DataBuffer::~DataBuffer()
{
    std::unique_lock<std::shared_mutex> lck(MapRWLock);
    delete ptrDataBuffer;
}

bool DataBuffer::getDeviceData(int32_t DeviceID,DynamicStruct& lastData)
{
    std::shared_lock<std::shared_mutex> lck(MapRWLock);
    std::map<int32_t,std::list<DynamicStruct>>::iterator findRes=ptrDataBuffer->find(DeviceID);
    if(findRes!=ptrDataBuffer->end())
    {
        lastData=*std::prev(findRes->second.end());
        return true;
    }
    else
        return false;
}

std::vector<std::pair<int32_t,DynamicStruct>> DataBuffer::avgSwapBuffer()  //返回的时候，字符串会被忽略掉
{
    std::vector<std::pair<int32_t,DynamicStruct>> resVector;
    std::map<int32_t,std::list<DynamicStruct>> copyMap;  //减少锁定时间
    {
        std::unique_lock<std::shared_mutex> lck(MapRWLock);
        copyMap=*ptrDataBuffer;
        delete ptrDataBuffer;
        ptrDataBuffer=new std::map<int32_t,std::list<DynamicStruct>>();
    }
    for(auto i : copyMap)
    {
        DynamicStruct avgStruct;
        avgStruct.cloneNum(*(i.second.begin()));
        for(DynamicStruct& j : i.second)
        {
            avgStruct.addNum(j);
        }
        avgStruct.divisionNum(i.second.size());
        resVector.push_back({i.first,avgStruct});
    }
    return resVector;
}

void DataBuffer::addData(const int32_t DeviceID,DynamicStruct Value)
{
    std::map<int32_t,std::list<DynamicStruct>>::iterator currentItem;
    {
        std::shared_lock<std::shared_mutex> lck(MapRWLock);   //读锁
        currentItem=ptrDataBuffer->find(DeviceID);
    }
    if(currentItem==ptrDataBuffer->end())  //找不到
    {
        std::list<DynamicStruct> newList;
        newList.push_back(Value);
        std::unique_lock<std::shared_mutex> lck(MapRWLock);
        ptrDataBuffer->insert({DeviceID,newList});
    }
    else
    {
        currentItem->second.push_back(Value);
    }
}

std::vector<int32_t> DataBuffer::getDeviceItems()
{
    std::vector<int32_t> deviceArr={};
    {
        std::shared_lock<std::shared_mutex> lk(MapRWLock);
        for(auto i : *ptrDataBuffer)
            deviceArr.push_back(i.first);
    }
    return deviceArr;
}

DataControl* DataControl::currentClass=nullptr;
SystemInfo DataControl::systemInfo;
std::map<int32_t,DeviceTypeItem> DataControl::DeviceTypeMap={};
std::map<int32_t,DeviceIDItem> DataControl::DeviceIDMap={};
std::shared_mutex DataControl::DeviceTypeMutex;
std::shared_mutex DataControl::DeviceIDMutex;

DataControl::DataControl()
{
    OATPP_LOGI("DataControl","DataControl Initialze");

    for(std::pair<int32_t,DeviceIDItem>& i : DBcontrol.downloadDeviceInfo())
        DeviceIDMap.insert(i);
    for(std::pair<int32_t,DeviceTypeItem>& i : DBcontrol.downloadDeviceType())
        DeviceTypeMap.insert(i);

    currentClass=this;
    timer.SetTime(std::chrono::seconds(60));
    timer.SetTask(TimerThread);
    timer.Start();
}

DataControl::~DataControl()
{
    timer.Stop();
}

void DataControl::TimerThread()
{
    std::vector<std::pair<int32_t,DynamicStruct>> NowTimeRes =  currentClass->dataBuffer.avgSwapBuffer();
    if(NowTimeRes.size()>0)
    {
        //OATPP_LOGI("DataControl","At Last 1 minutes, ReceiveData ID=%ld, Value=%f",NowTimeRes[0].first,NowTimeRes[0].second);
        for(auto i : NowTimeRes)
        {
            std::map<int32_t,DeviceIDItem>::iterator findIDRes;
            {
                std::shared_lock<std::shared_mutex> lck(DeviceIDMutex);
                findIDRes=DeviceIDMap.find(i.first);
            }
            if(findIDRes==DeviceIDMap.end())  //没有找到
            {
                OATPP_LOGI("DataControl","A unknown device upload data, DeviceID: %d",i.first);
                continue;
            }
            //上报数据
            currentClass->DBcontrol.uploadDeviceValue(findIDRes->first, i.second.toJson().dump());
        }
    }
    /*
    else
    {
        OATPP_LOGI("DataControl","At Last 1 minutes, No Data Received");
    }*/
}

void DataControl::addType0Data(int32_t DeviceID,double Value)
{
    dataBuffer.addData(DeviceID,{{"Value",Value}});
}

std::string DataControl::getLastTimeValue(double RequestData)
{
    DynamicStruct newStruct;
    if(dataBuffer.getDeviceData(RequestData,newStruct))
        return newStruct.toJson().dump();
    else
        return std::string();
}

struct JSONdeviceType
{
    int32_t TypeID;
    std::string TypeName;
    nlohmann::json FieldList;
    std::list<nlohmann::json> DeviceList;
};

std::string DataControl::getDeviceList()
{
    std::map<int32_t,DeviceTypeItem> copyTypeMap;
    std::map<int32_t,DeviceIDItem> copyDeviceMap;
    {
        std::shared_lock<std::shared_mutex> lk(DeviceIDMutex);
        copyDeviceMap=DeviceIDMap;
    }
    {
        std::shared_lock<std::shared_mutex> lk(DeviceTypeMutex);
        copyTypeMap=DeviceTypeMap;
    }

    std::unordered_map<int32_t,JSONdeviceType> TypeTree;
    for(auto i : copyTypeMap)
    {
        TypeTree.insert({
            i.first,
            {
                i.first,
                i.second.typeName,
                nlohmann::json(i.second.typeField),
                std::list<nlohmann::json>()
            }
        });
    }
    for(auto& i : copyDeviceMap)
    {
        auto ptr=TypeTree.find(i.second.deviceType);
        nlohmann::json deviceObject=nlohmann::json::object();
        deviceObject["DeviceID"]=i.first;
        deviceObject["DeviceName"]=i.second.deviceName;
        ptr->second.DeviceList.push_back(deviceObject);
    }

    std::list<nlohmann::json> itemList;
    for(auto& i : TypeTree)
    {
        nlohmann::json newObject=nlohmann::json::object();
        newObject["TypeID"]=i.second.TypeID;
        newObject["TypeName"]=i.second.TypeName;
        newObject["TypeField"]=i.second.FieldList;
        newObject["Devices"]=nlohmann::json(i.second.DeviceList);
        itemList.push_back(newObject);
    }
    return nlohmann::json(itemList).dump();
}

std::string DataControl::getHistoryData(int32_t DeviceID,int32_t timeLength)
{
    
    switch (timeLength)
    {
        case 1:  //1H
            {
                std::array<nlohmann::json,60> JSONlist;
                std::array<std::string,60> res=DBcontrol.downloadDeviceLast1Hour(DeviceID);
                for(int i=0;i<60;i++)
                {
                    if(res[i].length()>0)
                        JSONlist[i]=nlohmann::json::parse(res[i]);
                    else
                        JSONlist[i]=nlohmann::json::object();
                }
                return nlohmann::json(JSONlist).dump();
            }
            break;
        case 2:  //12H
            {
                std::array<nlohmann::json,24> JSONlist;
                std::array<DynamicStruct,24> res=DBcontrol.downloadDeviceLast12Hour(DeviceID,getDeviceTypeField(DeviceID));
                for(int i=0;i<24;i++)
                    JSONlist[i]=res[i].toJson();
                return nlohmann::json(JSONlist).dump();
            }
            break;
        case 3:  //24H
            {
                std::array<nlohmann::json,24> JSONlist;
                std::array<DynamicStruct,24> res=DBcontrol.downloadDeviceLast24Hour(DeviceID,getDeviceTypeField(DeviceID));
                for(int i=0;i<24;i++)
                    JSONlist[i]=res[i].toJson();
                return nlohmann::json(JSONlist).dump();
            }
            break;
        case 4:  //7D
            {
                std::array<nlohmann::json,7> JSONlist;
                std::array<DynamicStruct,7> res=DBcontrol.downloadDeviceLast7Day(DeviceID,getDeviceTypeField(DeviceID));
                for(int i=0;i<7;i++)
                    JSONlist[i]=res[i].toJson();
                return nlohmann::json(JSONlist).dump();
            }
        default:
            return std::string();
            break;
    }
}

std::vector<std::string> DataControl::getDeviceTypeField(int32_t DeviceID)
{
    int32_t typeID;
    {
        std::shared_lock<std::shared_mutex> lk(DeviceIDMutex);
        typeID=DeviceIDMap.find(DeviceID)->second.deviceType;
    }
    {
        std::shared_lock<std::shared_mutex> lk(DeviceTypeMutex);
        return DeviceTypeMap.find(typeID)->second.typeField;
    }
}

std::string DataControl::getServerInfo()
{
    nlohmann::json serverInfo;
    serverInfo["SysName"]=systemInfo.getSystemName();
    serverInfo["SysVersion"]=systemInfo.getSystemVersion();
    return serverInfo.dump();
}

std::string DataControl::getServerStatus()
{
    nlohmann::json ServerStatus;
    ServerStatus["CPU"]=systemInfo.getCPUusage();
    ServerStatus["MEM"]=systemInfo.getMemoryUsage();
    ServerStatus["DISK"]=systemInfo.getDiskUsage();
    return ServerStatus.dump();
}

void DataControl::addType2Data(int32_t DeviceID,double valueCPU,double valueMEM)
{
    dataBuffer.addData(DeviceID,{{"CPU",valueCPU},{"MEM",valueMEM}});
}